package cn.openea.perpartner.common.core.service.db.jdbc;

import cn.openea.perpartner.common.core.exception.ServiceException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.support.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * @author zane_ysk
 * 采用原生jdbc的方法进行批量处理
 */
@Data
@Slf4j
public class BatchInsert<T> {
    private static final int BATCH_INSERT_SIZE = 300;

    /**
     * 数据库连接
     */
    private Connection conn;

    private PreparedStatement ps;

    private PreparedStatement leftPs;
    /**
     * 前置sql
     */
    private String preSql;
    /**
     * 参数sql
     */
    private String endSql;
    /**
     * 每次插入条数
     */
    private int onceInsert = BATCH_INSERT_SIZE;
    /**
     * 目标List
     */
    private List<T> sourceList;

    private BatchInsertParams<T> batchInsertParams;

    private BatchInsert() {
    }

    public static class Builder {
        private BatchInsert batchInsert;

        public Builder() {
            batchInsert = new BatchInsert<>();
        }

        public Builder conn(Connection conn) {
            batchInsert.setConn(conn);
            return this;
        }

        public Builder preSql(String preSql) {
            batchInsert.setPreSql(preSql);
            return this;
        }

        public Builder endSql(String endSql) {
            batchInsert.setEndSql(endSql);
            return this;
        }

        public Builder onceInsert(int onceInsert) {
            batchInsert.setOnceInsert(onceInsert);
            return this;
        }

        public Builder list(List sourceList) {
            batchInsert.setSourceList(sourceList);
            return this;
        }

        public Builder batchInsertParams(BatchInsertParams batchInsertParams) {
            batchInsert.setBatchInsertParams(batchInsertParams);
            return this;
        }

        public BatchInsert build() {
            return batchInsert;
        }
    }


    public void batchInsert() {

        try {
            conn.setAutoCommit(false);
            int size = sourceList.size();
            int leftCount = size % onceInsert;
            String sql = getBatchSql(preSql, endSql, onceInsert);

            String leftSql = getBatchSql(preSql, endSql, leftCount);
            if (StringUtils.isNotEmpty(leftSql)) {
                leftPs = conn.prepareStatement(leftSql);
            }
            ps = conn.prepareStatement(sql);
            int insertCount = size / onceInsert;
            for (int i = 1; i <= insertCount; i++) {
                for (int j = 0; j < onceInsert; j++) {
                    batchInsertParams.setValues(ps, j, sourceList.get(j + (i - 1) * onceInsert));
                }
                ps.addBatch();
                ps.executeUpdate();
            }
            if (StringUtils.isNotEmpty(leftSql)) {
                for (int i = 0; i < leftCount; i++) {
                    batchInsertParams.setValues(leftPs, i, sourceList.get(i + insertCount * onceInsert));
                }
                leftPs.addBatch();
                leftPs.executeUpdate();
            }
            conn.commit();
        } catch (SQLException e) {
            log.error("error on batch insert", e);
            // 显示抛出 不然不回滚
            throw new ServiceException(e.getMessage());
        } finally {
            JdbcUtils.closeStatement(this.getLeftPs());
            close(conn, this.getPs(), null);
        }
    }

    /**
     * 获取批量sql
     *
     * @param preSql 前置sql
     * @param endSql 后置参数sql
     */
    private static String getBatchSql(String preSql, String endSql, int batchSize) {
        if (batchSize == 0) {
            return "";
        }
        StringBuilder sbSql = new StringBuilder(preSql);
        for (int i = 0; i < batchSize; i++) {
            sbSql.append(endSql);
            if (i == batchSize - 1) {
                sbSql.append(";");
            } else {
                sbSql.append(",");
            }
        }
        return sbSql.toString();
    }

    /**
     * 关闭连接
     */
    public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (pstmt != null) {
                pstmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
